
var copyArray = function(dest:any, doffset:any, src:any, soffset:any, length:any) {
    if('function' === typeof src.copy) {
      // Buffer
      src.copy(dest, doffset, soffset, soffset + length);
    } else {
      // Uint8Array
      for(var index=0; index<length; index++){
        dest[doffset++] = src[soffset++];
      }
    }
};

var EgretReadByteArray = class EgretReadByteArray{
    private buffer:any
    private dataview:any
    private offset:any
    constructor(buffer:any){
        this.buffer = buffer;
        this.dataview = new DataView(this.buffer);
        this.offset = 0;
    }

    readByte(){
        var ret = this.dataview.getUint8(this.offset);
        this.offset += 1;
        return ret;
    }
    
    readShort(){
        var ret = this.dataview.getInt16(this.offset);
        this.offset += 2;
        return ret;
    }
    
    readInt(){
        var ret = this.dataview.getInt32(this.offset);
        this.offset += 4;
        return ret;
    }
    
    readUTF(){
        var length = this.dataview.getInt16(this.offset);
        this.offset += 2;
        var tempBuffer = this.buffer.slice(this.offset, this.offset+length);
       //cc.error("length",this.offset, length, tempBuffer, tempBuffer.byteLength);
        this.offset += length;
        var ret = this.strdecode(tempBuffer);
        return ret;
    }

    readUTFByte(){
        var length = this.dataview.getInt8(this.offset);
        this.offset += 1;
        var tempBuffer = this.buffer.slice(this.offset, this.offset+length);
       //cc.error("length",this.offset, length, tempBuffer, tempBuffer.byteLength);
        this.offset += length;
        var ret = this.strdecode(tempBuffer);
        return ret;
    }
    
    // // ArrayBuffer转为字符串，参数为ArrayBuffer对象
    // ab2str:function(buf) {
    //     return String.fromCharCode.apply(null, new Uint16Array(buf));
    // },
    
    /**
   * client decode
   * msg String data
   * return Message Object
   */
    strdecode(buffer:any) {
        var bytes = new Uint8Array(buffer);
        var array : any[]=[];
        var offset = 0;
        var charCode = 0;
        var end = bytes.length;
        //cc.error('befor Protocol.strdecode length:', end);
        var hasEot = false;
        while(offset < end){
            try{
            if(bytes[offset] < 128){
                charCode = bytes[offset];
                offset += 1;
                if (charCode == 4) {
                // cc.error('find strdecode eot');
                hasEot = true;
                continue;
                }
            }else if(bytes[offset] < 224){
                charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
                offset += 2;
            }else{
                charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
                offset += 3;
            }
            array.push(charCode);  
            }
            catch(e){
            // cc.error(e);
            }
        }
        if (hasEot)
            array.push("}");
            
        var ret = String.fromCharCode.apply(null, array);
        //cc.error('after Protocol.strdecode length:', array.length, array, ret);
        return ret;
    }
}

var EgretWriteByteArray = class EgretWriteByteArray {
    private buffer:any
    private dataview:any
    private offset:any

    constructor(length:number){
        this.buffer = new ArrayBuffer(length);
        this.dataview = new DataView(this.buffer);
        this.offset = 0;
    }
    
    getBuffer(){
        //cc.error(this.buffer.byteLength, this.offset);
        var retBuffer = this.buffer.slice(0, this.offset);
        //cc.error("getBuffer", retBuffer);
        return this.buffer;
    }
    
    writeByte(value:any){
        var ret = this.dataview.setUint8(this.offset, value);
        this.offset += 1;
        //cc.error('offset:', this.offset);
    }
    
    writeShort(value:any){
        var ret = this.dataview.setInt16(this.offset, value);
        this.offset += 2;
        //cc.error('offset:', this.offset);
    }
    
    writeInt(value:any){
        var ret = this.dataview.setInt32(this.offset, value);
        this.offset += 4;
        //cc.error('offset:', this.offset);
    }
    
    writeUTF(str:any){
        //cc.error(this.dataview, str, str.length);
        var strBuffer = this.strencode(str);
        this.writeShort(strBuffer.length);
        copyArray(new Uint8Array(this.buffer), this.offset, strBuffer, 0, strBuffer.length);
        this.offset += strBuffer.length;
        
        //cc.error(this.dataview, strBuffer.length);
        //cc.error('offset:', this.offset);
    }

    strencode(str:any) {
        var byteArray = new Uint8Array(str.length * 3);
        var offset = 0;
        for(var i = 0; i < str.length; i++){
          var charCode = str.charCodeAt(i);
          var codes = null;
          if(charCode <= 0x7f){
            codes = [charCode];
          }else if(charCode <= 0x7ff){
            codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
          }else{
            codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
          }
          for(var j = 0; j < codes.length; j++){
            byteArray[offset] = codes[j];
            ++offset;
          }
        }
        var _buffer = new Uint8Array(offset);
        copyArray(_buffer, 0, byteArray, 0, offset);
        return _buffer;
    }
}

export class NetWork {

    private socket : WebSocket | undefined = undefined;

    private handshakeCallback:any;
    private eventCallback:any;

    Connect(url:string ,handshakeCallback:any,eventCallback:any){
        this.socket = new WebSocket(url);
        this.socket.binaryType = 'arraybuffer';
		this.socket.onopen = this.OnOpen.bind(this);
		this.socket.onmessage = this.OnMessage.bind(this);
		this.socket.onerror = this.OnError.bind(this);
		this.socket.onclose = this.OnClose.bind(this);

        this.handshakeCallback = handshakeCallback;
        this.eventCallback = eventCallback;
    }

    OnOpen(){
        
        this.handshakeCallback()
        
    }

    OnMessage(event:any){
        let byte ;
        let messageType = 0;
        let eventName = "";
        let statusCode=0;
        let strBody;
        try {
            byte = new EgretReadByteArray(event.data);
            messageType = byte.readShort();
            eventName = byte.readUTFByte().toLocaleLowerCase();
            statusCode = byte.readShort();
            strBody = byte.readUTF();

            // console.log(strBody)
            
        } catch (error) {
            console.error(error)
        }

        if(statusCode){
            this.eventCallback("OnCodeError", [eventName, statusCode, strBody]);
        }
        else{
            this.eventCallback("OnReceive", [eventName, strBody]);
        }
        
    }

    OnError(event:any){

    }

    OnClose(event:any){

    }

    write(head:any, message:string){
        var strMsg = message;
        var length = 1 + 2 + 2 + head.event.length * 3 + 2 + strMsg.length * 3;
        // console.log('[send]: length %s, event:%s, length:%s, msg:%s, length:%s', length, head.event, strMsg, head.event.length, strMsg.length);
		var byte = new EgretWriteByteArray(length);

        //创建 ByteArray 对象
		byte.writeByte(head.messageType);//消息类型
		byte.writeShort(head.sequence);//消息序列号
		byte.writeUTF(head.event);//数据类型
		byte.writeUTF(strMsg);//消息体
        this.socket?.send(byte.getBuffer())
		//发送数据
		// this.socket.send(byte.getBuffer());
    }

}